#include <sys/epoll.h>
#include "event_dispatcher.h"
#include "event_loop.h"

#define MAXEVENTS 128

typedef struct
{
    int event_count;            // 事件个数
    int nfds;                   // fd 个数
    int realloc_copy;           // 重新分配次数
    int efd;                    // epoll fd
    struct epoll_event *events; // 监听事件
} epoll_dispatcher_data;

static void *epoll_init(struct event_loop *);

static int epoll_add(struct event_loop *, struct channel *chan);
static int epoll_del(struct event_loop *, struct channel *chan);
static int epoll_update(struct event_loop *, struct channel *chan);
static int epoll_dispatch(struct event_loop *, struct timeval *);
static void epoll_clear(struct event_loop *);

// epoll 派发器
const struct event_dispatcher epoll_dispatcher = {
    "epoll",
    epoll_init,
    epoll_add,
    epoll_del,
    epoll_update,
    epoll_dispatch,
    epoll_clear,
};

void *epoll_init(struct event_loop *eloop)
{
    epoll_dispatcher_data *dispacther_data = malloc(sizeof(epoll_dispatcher_data));

    dispacther_data->event_count = 0;
    dispacther_data->nfds = 0;
    dispacther_data->realloc_copy = 0;
    dispacther_data->efd = 0;

    dispacther_data->efd = epoll_create1(0); // 新建 epoll
    if (dispacther_data->efd == -1)
    {
        error(1, errno, "epoll create failed");
    }
    dispacther_data->events = calloc(MAXEVENTS, sizeof(struct epoll_event));
    return dispacther_data;
}

int epoll_add(struct event_loop *eloop, struct channel *chan)
{
    epoll_dispatcher_data *dispatcher_data = (epoll_dispatcher_data *)eloop->event_dispatcher_data;
    int fd = chan->fd;
    int events = 0;
    if (chan->events & EVENT_READ)
    {
        events = events | EPOLLIN;
    }
    if (chan->events & EVENT_WRITE)
    {
        events = events | EPOLLOUT;
    }
    struct epoll_event event;
    event.data.fd = fd;
    event.events = events;
    // epoll_ctl
    if (epoll_ctl(dispatcher_data->efd, EPOLL_CTL_ADD, fd, &events) == -1)
    {
        error(1, errno, "epoll_ctl add fd failed");
    }
    return 0;
}

int epoll_del(struct event_loop *eloop, struct channel *chan)
{
    epoll_dispatcher_data *dispatcher_data = (epoll_dispatcher_data *)eloop->event_dispatcher_data;
    int fd = chan->fd;
    int events = 0;
    if (chan->events & EVENT_READ)
    {
        events = events | EPOLLIN;
    }
    if (chan->events & EVENT_WRITE)
    {
        events = events | EPOLLOUT;
    }
    struct epoll_event event;
    event.data.fd = fd;
    event.events = events;
    // epoll_ctl
    if (epoll_ctl(dispatcher_data->efd, EPOLL_CTL_DEL, fd, &events) == -1)
    {
        error(1, errno, "epoll_ctl delete fd failed");
    }
    return 0;
}

int epoll_update(struct event_loop *eloop, struct channel *chan)
{
    epoll_dispatcher_data *dispatcher_data = (epoll_dispatcher_data *)eloop->event_dispatcher_data;
    int fd = chan->fd;
    int events = 0;
    if (chan->events & EVENT_READ)
    {
        events = events | EPOLLIN;
    }
    if (chan->events & EVENT_WRITE)
    {
        events = events | EPOLLOUT;
    }
    struct epoll_event event;
    event.data.fd = fd;
    event.events = events;
    // epoll_ctl
    if (epoll_ctl(dispatcher_data->efd, EPOLL_CTL_MOD, fd, &events) == -1)
    {
        error(1, errno, "epoll_ctl mofify fd failed");
    }
    return 0;
}

int epoll_dispatch(struct event_loop *eloop, struct timeval *timeval)
{
    epoll_dispatcher_data *dispatcher_data = (epoll_dispatcher_data *)eloop->event_dispatcher_data;
    int i, n;
    n = epoll_wait(dispatcher_data->efd, dispatcher_data->events, MAXEVENTS, timeval);
    for (i = 0; i < n; i++)
    {
        if ((dispatcher_data->events[i].events & EPOLLERR) || (dispatcher_data->events[i].events & EPOLLHUP))
        {
            fprintf(stderr, "epoll error\n");
            close(dispatcher_data->events[i].data.fd);
            continue;
        }
        // 读
        if (dispatcher_data->events[i].events & EPOLLIN)
        {
            // 激活 channel 读事件
            channel_event_activate(eloop, dispatcher_data->events[i].data.fd, EVENT_READ);
        }
        // 写
        if (dispatcher_data->events[i].events & EPOLLIN)
        {
            // 激活 channel 读事件
            channel_event_activate(eloop, dispatcher_data->events[i].data.fd, EVENT_WRITE);
        }
    }
    return 0;
}

void epoll_clear(struct event_loop *eloop)
{
    epoll_dispatcher_data *dispatcher_data = (epoll_dispatcher_data *)eloop->event_dispatcher_data;
    free(dispatcher_data->events);
    close(dispatcher_data->efd);
    free(dispatcher_data);
    eloop->event_dispatcher_data = NULL;
}